/*
============================================================================
 Name        : nmsHuffman.c
 Author      : Jian
 Version     :
 Copyright   : base on http://ce.sharif.edu/~ahmadinejad/jpeg/
 Description : nmsHuffman.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "nmsHuffman.h"
#include "nmsJpegUtil.h"
#include "nmsJpegMcuReader.h"


static nmsHuffman* _readHuffmanTable(HFILE fileHandle, int* isAc, int* index)
{
    nmsHuffman* hufm = NULL ;
    UINT8 byte       = -1 ;
    int i            = 0 ;
    UINT8 count[17]  = {0} ;
    UINT16 code      = 0 ;

    hufm = (nmsHuffman*)nmsJpegMalloc(sizeof(nmsHuffman)) ;
    nmsMemSet(hufm, 0, sizeof(nmsHuffman)) ;

    nmsJpegReadFile(fileHandle, &byte, 1) ;

    if ((byte >> 5) != 0) {
        nprintf("error: read huffman (%d >> 5) != 0", byte) ;
        goto _error ;
    }

    *isAc  = (byte & 0x10) ;
    *index = (byte & 0x0F) ;
    if (*index > __NMS_MAX_DHT_INDEX) {
        nprintf("error: read huffman index = %d is not correct", *index) ;
        goto _error ;
    }

    nmsJpegReadFile(fileHandle, &(count[1]), 16) ;

    for (i = 1; i <= 16; i++) {
        if (count[i] != 0) {
            hufm->firstCodeVal[i] = code ;
            hufm->symbolIndex[i]  = hufm->symbolCount ;
            hufm->symbolCount += count[i] ;
            code += count[i] ;
            hufm->checkCount[i] = count[i] ;
            hufm->bitWidth[i]   = i ;
        } else {
            hufm->firstCodeVal[i] = hufm->firstCodeVal[i - 1] ;
            hufm->symbolIndex[i]  = hufm->symbolIndex[i - 1] ;
            hufm->checkCount[i]   = hufm->checkCount[i - 1] ;
            hufm->bitWidth[i]     = hufm->bitWidth[i-1] ;
        }

        code = code << 1 ;
    }

    hufm->symbol = (UINT8*)nmsJpegMalloc(hufm->symbolCount) ;

    nmsJpegReadFile(fileHandle, hufm->symbol, hufm->symbolCount) ;

    return hufm ;

_error:

    nmsJpegHuffmanFree(hufm) ;
    return NULL ;
}


int nmsJpegHuffmanReadFrom(HFILE fileHandle, struct __nms_huffman* acTable[], struct __nms_huffman* dcTable[])
{
    nmsHuffman* hufm = NULL ;
    int len          = 0 ;
    int i            = 2 ;
    int isAc         = 0 ;
    int index        = 0 ;

    len = nmsJpegRead2ByteBigEndian(fileHandle) ;

    if (len < 19) {
        nprintf("error: read huffman len = %d is not correct", len) ;
        return -1 ;
    }

    /* NOTE: one DHT maker may contain more than 1 huffman table */
    while (i < len) {

        hufm = _readHuffmanTable(fileHandle, &isAc, &index) ;

        if (!hufm)  return -1 ;

        i += (1 + 16 + hufm->symbolCount) ;

        if (isAc)
            acTable[index] = hufm ;
        else
            dcTable[index] = hufm ;
    }

    return 0;
}

void nmsJpegHuffmanFree(nmsHuffman* hufm)
{
    if (hufm) {
        if (hufm->symbol)  nmsJpegFree(hufm->symbol) ;
        nmsJpegFree(hufm) ;
    }
}


int nmsJpegHuffmanParse(const nmsHuffman* hufm, struct __nms_jpeg_mcu* mcu, int isAc, int* val, UINT8* zeroCount)
{
    UINT16 dData = mcu->curData ;
    int code = 0 ;
    int valBitLen  = 1 ;
    int symbIndex  = 0 ;
    int tmpVal     = 0 ;

    /* TODO: count from 16 */
    code = (dData >> (16 - valBitLen)) & 1;

    while(code >= hufm->firstCodeVal[valBitLen] && valBitLen <= 16) {
        code <<= 1;
        code |= ((dData >> (16 - ++valBitLen)) & 1);
    }
    valBitLen = hufm->bitWidth[valBitLen - 1];

    __NMS_ASSERT_DEFENSE(valBitLen <= 16, return -1) ;

    nmsMcuEatBits(mcu, valBitLen) ;

    symbIndex = (dData >> (16 - valBitLen)) - hufm->firstCodeVal[valBitLen] ;
    __NMS_ASSERT_DEFENSE(symbIndex < hufm->checkCount[valBitLen], return -1) ;

    symbIndex = hufm->symbolIndex[valBitLen] + symbIndex ;
    __NMS_ASSERT_DEFENSE(symbIndex < hufm->symbolCount, return -1) ;

    code = hufm->symbol[symbIndex] ;

    dData = mcu->curData ;

    if (isAc) {
        *zeroCount = ((UINT8)code) >> 4 ;
        valBitLen     = ((UINT8)code) & 0xF ;

        if (valBitLen == 0) {
            *val = 0 ;
            return (*zeroCount == 0) ? __EOB_FLAG : 0 ;
        }
    } else {
        valBitLen = code ;
    }

    tmpVal = (short)(dData >> (16 - valBitLen)) ;
    if (!(tmpVal & (1<< (valBitLen-1)))) {
        tmpVal|=(-(1<<(valBitLen)));
        tmpVal++;
    }

    *val    = tmpVal ;
    nmsMcuEatBits(mcu, valBitLen) ;

    return 0 ;
}


nmsHuffmanBuilder* nmsHuffmanBuild(const UINT8* htCount,const UINT8* htVals, UINT8 maxVal)
{
    nmsHufmmanVal* valArray = NULL ;
    int posHtVals    = 0 ;
    UINT16 codeValue = 0 ;
    int i, j ;

#ifdef __NMS_JPEG_DEBUG
    nmsHuffmanBuilder* hum = (nmsHuffmanBuilder*)nmsJpegMalloc(sizeof(nmsHuffmanBuilder)) ;
    hum->valCount          = maxVal + 1 ;
    hum->htVals            = htVals ;
    hum->valArray          = (nmsHufmmanVal*)nmsJpegMalloc(hum->valCount * sizeof(nmsHufmmanVal)) ;
    nmsMemSet(hum->valArray, 0, hum->valCount * sizeof(nmsHufmmanVal)) ;
    valArray               = hum->valArray ;
#else
    nmsHuffmanBuilder* hum = (nmsHuffmanBuilder*)nmsJpegMalloc((maxVal + 1) * sizeof(nmsHufmmanVal)) ;
    nmsMemSet(hum, 0, (maxVal + 1) * sizeof(nmsHufmmanVal)) ;
    valArray               = hum ;
#endif

    for (i = 1; i <= 16; i++) {
        for (j = 1; j <= htCount[i]; j++) {
            valArray[htVals[posHtVals]].code     = codeValue;
            valArray[htVals[posHtVals]].bitLen   = i;
#ifdef __NMS_JPEG_DEBUG
            valArray[htVals[posHtVals]].posInHt  = posHtVals ;
#endif
            posHtVals++;
            codeValue++;
        }
        codeValue <<= 1;
    }

    return hum ;
}

#ifdef __NMS_JPEG_DEBUG

nmsHufmmanVal nmsHuffmanGet(nmsHuffmanBuilder* hum, UINT8 val)
{
    nmsHufmmanVal ret ;
    ret.bitLen = 0 ;
    ret.code   = -1 ;
    __NMS_ASSERT_DEFENSE(val < hum->valCount, return ret) ;

    ret =  hum->valArray[val] ;

    __NMS_ASSERT(ret.bitLen != 0) ;
    __NMS_ASSERT(hum->htVals[ret.posInHt] == val)  ;

    return ret;
}

void nmsHuffmanBuilderFree(nmsHuffmanBuilder* hum)
{
    if (hum) {
        nmsJpegFree(hum->valArray) ;
        nmsJpegFree(hum) ;
    }
}

#endif


/* End of File */
